package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.constant.Constants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceDataMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ Description: 设备服务层
 * @ BelongsProject: zzyl-cp
 * @ Author: G攀
 * @ CreateTime: 2024-09-09  11:46
 * @ Version: 1.0
 */
@Service
public class DeviceServiceImpl implements DeviceService {

    // 读取物联网平台的设备信息
    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;

    @Autowired
    private Client client;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceDataMapper deviceDataMapper;

    /**
     * 从物联网平台同步数据
     */
    @Override
    public void syncProductList() {
        // 请求参数
        QueryProductListRequest request = new QueryProductListRequest();
        request.setPageSize(200);
        request.setCurrentPage(1);
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        // 调用第三方查询产品列表
        QueryProductListResponse response;

        try {
            response = client.queryProductList(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }

        // 判断数据是否为空，如果数据为空，中断请求
        if (!response.body.success) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        // 获取数据，存储到redis
        List<ProductVo> productVoList = response.getBody().getData().getList().getProductInfo().stream().map((p -> BeanUtil.toBean(p, ProductVo.class))).collect(Collectors.toList());

        // 存储到redis中
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(productVoList));

    }

    /**
     * 查询所有的产品列表
     */
    @Override
    public List<ProductVo> allProduct() {
        String json = (String) redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        List<ProductVo> list = JSONUtil.toList(json, ProductVo.class);
        return list;
    }

    /**
     * 注册设备
     */
    @Override
    public void RegisterDevice(DeviceDto dto) {
        //1、根据设备名字查找数据库是否存在数据，返回数量，数量>0，表示存在
        Long count = deviceMapper.countByDeviceName(dto.getDeviceName());
        if (count > 0) {
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }

        //2、调用阿里云IOT注册设备。
        // 获取注册需要的参数，前端已经传过来。 RegisterDeviceRequest 物联网SDK提供
        RegisterDeviceRequest registerDeviceRequest = dto.getRegisterDeviceRequest();
        // 设置IOT实例ID
        registerDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse registerDeviceResponse = null;
        try {
            registerDeviceResponse = client.registerDevice(registerDeviceRequest);
        } catch (Exception e) {
            // 物联网设备注册失败
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        // 判断一下是否调通
        if (registerDeviceResponse == null || !registerDeviceResponse.getBody().success) {
            throw new RuntimeException(registerDeviceResponse.getBody().getErrorMessage());
        }

        // 获取响应数据设备ID，封装到dto中
        dto.setIotId(registerDeviceResponse.getBody().getData().getIotId());

        //3、调用阿里云IOT查询所属产品的名字
        QueryProductRequest queryProductRequest = new QueryProductRequest();
        queryProductRequest.setProductKey(dto.getProductKey());
        queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryProductResponse queryProductResponse = null;
        try {
            queryProductResponse = client.queryProduct(queryProductRequest);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        // getSuccess 成功返回true
        if (!queryProductResponse.getBody().success) {
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        // 封装产品的名字
        dto.setProductName(queryProductResponse.getBody().getData().getProductName());

        // 将DeviceDto赋值给Device
        Device device = BeanUtil.toBean(dto, Device.class);

        //4、判断设备类型是否为0，如果为0，设置物理类型为-1（具体的老人）
        if (device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())) {
            // 是随身设备, 把物理位置绑定成老人，方便后期根据位置字段查找老人数据
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }

        // 设置门禁，本项目没门禁
        device.setHaveEntranceGuard(Constants.DEFAULT_ENTRANCE_GUARD);

        //5、设备插入数据库
        try {
            deviceMapper.insertSelective(device);
        } catch (Exception e) {
            //6、插入发生异常，调用阿里云删除设备
            e.printStackTrace();
            // 本地数据库插入失败，删除iot中的设备数据
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setDeviceName(device.getDeviceName());
            deleteDeviceRequest.setIotId(device.getIotId());
            deleteDeviceRequest.setProductKey(device.getProductKey());
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

            try {
                client.deleteDevice(deleteDeviceRequest);
            } catch (Exception ex) {
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
            // 走到这里，老人已经绑定产品，不能删除，请重新选择。
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);
        }
    }

    /**
     * 分页查询设备
     */
    @Override
    public ResponseResult<PageResponse> page(DevicePageQueryDto pageQueryDto) {
        // 设置分页条件
        PageHelper.startPage(pageQueryDto.getPageNum(), pageQueryDto.getPageSize());
        // 正常的查询
        Page<DeviceVo> deviceVoPage = deviceMapper.page(pageQueryDto);
        // 封装返回条件
        PageResponse<DeviceVo> pageResponse = PageResponse.of(deviceVoPage, DeviceVo.class);
        return ResponseResult.success(pageResponse);
    }

    /**
     * 查询设备详情数据
     */
    @Override
    public ResponseResult<DeviceVo> queryDeviceDetail(QueryDeviceDetailRequest queryDeviceDetailRequest) {
        // 根据设备ID从数据库中查询
        DeviceVo deviceVo = deviceMapper.queryDeviceDetailByIotId(queryDeviceDetailRequest.getIotId());

        // 设置实例ID
        queryDeviceDetailRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDeviceDetailResponse response = null;
        try {
            response = client.queryDeviceDetail(queryDeviceDetailRequest);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        // 判断接口是否调通
        if (!response.getBody().getSuccess()) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }

        // 获取远程的数据
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = response.getBody().getData();

        BeanUtil.copyProperties(data, deviceVo, CopyOptions.create().ignoreNullValue());

        return ResponseResult.success(deviceVo);
    }

    /**
     * 查询指定设备的物模型运行状态
     */
    @Override
    public ResponseResult queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {
        // 设置实例ID
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        QueryDevicePropertyStatusResponse response = null;
        try {
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }

        // 判断接口是否调通
        if (!response.getBody().getSuccess()) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }

        QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyData data = response.getBody().getData();

        return ResponseResult.success(data);
    }

    /**
     * - 查看指定产品的已发布物模型中的功能定义详情
     */
    @Override
    public ResponseResult queryThingModelPublishedRequest(QueryThingModelPublishedRequest request) {
        // 设置实例ID
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());


        QueryThingModelPublishedResponse response = null;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }

        // 判断接口是否调通
        if (!response.getBody().getSuccess()) {
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }

        QueryThingModelPublishedResponseBody.QueryThingModelPublishedResponseBodyData data = response.getBody().getData();

        return ResponseResult.success(data);
    }

    /**
     * 修改设备备注名称
     * 加上事务回滚，如果远程更改抛异常，不进行插入数据库
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void UpdateDevice(DeviceDto dto) {
        //1、根据设备名字查找数据库是否存在数据，返回数量，数量<=0，表示不存在，不能修改
        Long count = deviceMapper.countByDeviceName(dto.getDeviceName());
        if (count <= 0) {
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }

        //设备更新
        try {


            //1、调用阿里云IOT 修改设备昵称
            BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();

            // 创建对象，封装请求参数
            BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo info = new BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo();
            info.setNickname(dto.getNickname());
            info.setIotId(dto.getIotId());

            // 这里记得创建一个集合，参数需要封装成集合
            List<BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo> infoList = new ArrayList<>();
            infoList.add(info);

            // 设置IOT实例ID
            request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            // 设置iotId
            request = request.setDeviceNicknameInfo(infoList);


            BatchUpdateDeviceNicknameResponse budnResponse = null;
            try {
                budnResponse = client.batchUpdateDeviceNickname(request);
            } catch (Exception e) {
                // 物联网设备修改失败，后续可以发送短信通知
                throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
            }

            // 判断一下是否调通
            if (budnResponse == null || !budnResponse.getBody().success) {
                throw new RuntimeException(budnResponse.getBody().getErrorMessage());
            }

            // 更新数据，不用事务，事务主要用来管同一个方法，对多张表进行多次增删改操作的。
            Device device = BeanUtil.toBean(dto, Device.class);
            deviceMapper.updateSelective(device);

        } catch (Exception e) {
            //6、更新发生异常
            e.printStackTrace();
            // 抛出更新设备异常
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
    }

    /**
     *  删除设备
     */
    @Override
    public void DeleteDevice(DeleteDeviceRequest deleteDeviceRequest) {
        //1、根据IotId查找数据库是否存在数据，返回数量，数量<=0，表示不存在，不能修改
        String iotId = deleteDeviceRequest.getIotId();
        Long count = deviceMapper.countByIotId(iotId);
        if (count <= 0) {
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }

        //2、删除设备
        try {

            //2、调用阿里云IOT 删除设备
            // 设置IOT实例ID
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            DeleteDeviceResponse deleteResponse = null;
            try {
                deleteResponse = client.deleteDevice(deleteDeviceRequest);
            } catch (Exception e) {
                // 物联网设备修改失败，后续可以发送短信通知
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }

            // 判断一下是否调通
            if (deleteResponse == null || !deleteResponse.getBody().success) {
                throw new RuntimeException(deleteResponse.getBody().getErrorMessage());
            }

            // 删除设备表中的设备
            deviceMapper.deleteSelective(iotId);
        } catch (Exception e) {
            //删除发生异常
            e.printStackTrace();
            // 走到这里，老人已经绑定产品，不能删除，请重新选择。
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
    }

    /**
     * 按天统计查询指标数据
     */
    @Override
    public ResponseResult queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {
        // 1、将long类型转换成LocalDateTime
        LocalDateTime start = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);

        //2、调用Mapper查询数据库已有的每个小时统计物模型平均数据值，返回List<DeviceDataGraphVo> list1
        List<DeviceDataGraphVo> list1 = deviceDataMapper.queryDeviceDataListByDay(iotId, functionId, start, end);

        //3、按24小时构建List<DeviceDataGraphVo>("00:00"，“01:00”，....，“23:00”) list2
        List<DeviceDataGraphVo> list2 = DeviceDataGraphVo.dayInstance(start);

        //4、遍历list2中每个元素，将每个元素去list1匹配相同时间数据值赋值给list2中相同时间的数据值，不匹配赋值为0
        for (DeviceDataGraphVo deviceDataGraphVo : list2) {
            for (DeviceDataGraphVo dataGraphVo : list1) {
                if (deviceDataGraphVo.getDateTime().equals(dataGraphVo.getDateTime())) {
                    deviceDataGraphVo.setDataValue(dataGraphVo.getDataValue());
                    // 有值赋值就可以，工具类中的方法已经设置好默认值0
                }
            }
        }

        //5、返回数据list2
        return ResponseResult.success(list2);
    }

    @Override
    public ResponseResult queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {
        // 1、将long类型转换成LocalDateTime
        LocalDateTime start = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);

        //2、调用Mapper查询数据库已有的每个小时统计物模型平均数据值，返回List<DeviceDataGraphVo> list1
        List<DeviceDataGraphVo> list1 = deviceDataMapper.queryDeviceDataListByWeek(iotId, functionId, start, end);

        //3、按照天构建 list2
        List<DeviceDataGraphVo> list2 = DeviceDataGraphVo.weekInstance(start);

        //4、遍历list2中每个元素，将每个元素去list1匹配相同时间数据值赋值给list2中相同时间的数据值，不匹配赋值为0
        for (DeviceDataGraphVo deviceDataGraphVo : list2) {
            for (DeviceDataGraphVo dataGraphVo : list1) {
                if (deviceDataGraphVo.getDateTime().equals(dataGraphVo.getDateTime())) {
                    deviceDataGraphVo.setDataValue(dataGraphVo.getDataValue());
                    // 有值赋值就可以，工具类中的方法已经设置好默认值0
                }
            }
        }

        //5、返回数据list2
        return ResponseResult.success(list2);
    }
}
